#ifndef MEMTRACE_H_
#define MEMTRACE_H_

#include <iostream>
#include <memory>
#include <fstream>
#include <sstream>
#include <zlib.h>
#include <cassert>
#include <map>
#include <vector>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include "../../engine/address.hpp"
#include "../../engine/ui.hpp"
#include "../../modules/cpus/ins/naming.hpp"
#include "../../modules/cpus/ins/trace-reader.hpp"
#include "../../../../utils/exceptions.hpp"
#include "../../../../utils/gzfile.hpp"

#include "constants.h"

using namespace std;


class ParseError : public std::runtime_error {
public:
    ParseError(const std::string &msg) : std::runtime_error(msg.c_str()) { }
};

class  Pageobj {
    public:
        uint32_t _page;
        uint32_t _access;
    public:
        Pageobj(){_page=_access=0;}
        Pageobj(uint32_t page, uint32_t access) {
                _page = page;
                _access = access;
        }
        void setData(uint32_t page, uint32_t access) {
                _page = page;
                _access = access;
        }
        bool operator< (const Pageobj & element) {
            if (_access < element._access) return true; return false;
        }
};

typedef struct Access {
    uint64_t address;
    uint32_t memOpcode; //LD=0 ST=1
}Access;

class Trace {
    private:
        std::map<uint64_t,uint32_t> *_table; // [thread],page,accesses
        std::vector<Access> *_mem;         // page accesses sorted by program access
        std::vector<uint32_t> *_mutex;      // number of accesses per thread where a mutex occurs
        std::vector<Pageobj> *_memsort;     // page accesses sorted by most accessed
        uint32_t _threads;
        uint32_t _pageSize;
        uint64_t _gAccesses;
        /* PreProcess file variables*/
        uint32_t *_region;
        uint32_t *_threadIndex;
        uint32_t _traceIterations;
        uint32_t *_iteration;
        std::auto_ptr< std::istream > * _is;
        uint32_t * _traceRegionLines;
        bool _skipwrite;
        bool _usePages;
        char _dirname[1024];
        pthread_mutex_t _mutex1;

    public:
        Trace(int threads,uint32_t pageSize);
        ~Trace();
        void reset();
        /* utils */
        bool has_suffix(const std::string& path, const std::string& suffix);
        std::string replaceRegionName(std::string region);
        uint32_t threadFromRegion(std::string region);
        std::auto_ptr< std::istream > create_istream(const std::string& path);
        /* core */
        int preProcess(uint32_t);
        int preProcessFile(const std::string rwefecdir,const uint32_t tid);
        uint32_t startNextTraceIteration(uint32_t tid);
        void setParams(const char *dirname,uint32_t gThreads,bool skipwrite,
                       bool usePages,uint32_t iterations);
        void getDirname(char *dirname);
        inline bool getSkipwrite() { return _skipwrite;}
        bool access(uint32_t thread,uint64_t page,uint32_t memopcode);
        void setMutex(uint32_t thread);
        bool isMutex(uint32_t thread,uint32_t access);
        uint64_t getAccess(uint32_t thread,uint64_t page);
        inline uint64_t getgAccesses() {return _gAccesses;}
        inline uint32_t getpageSize() {return _pageSize;}
        uint32_t getPageAccess(uint32_t thread,uint32_t index);
        bool getMemoryAccess(uint32_t thread,uint32_t index,uint64_t &address,
                                      uint32_t &memopcode);
        uint32_t getAccessSize(uint32_t thread);
        void print(char * benchmark,ofstream & outfile,bool pagesmap);
        void sortMap();
        uint32_t vectorthreadSize(uint32_t thread);
        uint32_t vectorthreadPage(uint32_t thread,uint32_t index);
        uint32_t getMaxAccess(uint32_t thread,uint32_t index);
        uint32_t getMaxAccessPage(uint32_t thread,uint32_t index);
        inline uint32_t getRegion(uint32_t tid) const {return _region[tid];}
};
#endif /*MEMTRACE_H_*/
